Skip to content

Fix NonlinearSolveAlg crashing/stalling with polyalgorithm inner solvers - #3959

Merged
ChrisRackauckas merged 2 commits into
SciML:masterfrom
singhharsh1708:fix-nonlinearsolvealg-polyalg-3859
Aug 1, 2026
Merged

Fix NonlinearSolveAlg crashing/stalling with polyalgorithm inner solvers#3959
ChrisRackauckas merged 2 commits into
SciML:masterfrom
singhharsh1708:fix-nonlinearsolvealg-polyalg-3859

Conversation

@singhharsh1708

@singhharsh1708 singhharsh1708 commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Fixes the FieldError crash in #3859.

NonlinearSolvePolyAlgorithmCache (RobustMultiNewton, FastShortcutNonlinearPolyalg) keeps u/fu on its active subcache, not at top level, so every raw cache.cache.u-style read in newton.jl threw FieldError the moment a polyalgorithm was used as the NonlinearSolveAlg inner solver. Its reinit! also left force_stop/retcode set after a branch converged once, so a step!-driven loop silently no-oped forever after and reused a stale iterate.

Per review, both problems are fixed at the interface level in NonlinearSolve.jl#1115 (polyalg get_u/get_fu accessors plus the force_stop/retcode reset in reinit!), merged and released as NonlinearSolveBase 2.39.0. This PR is just the consumer side: every polyalg-unsafe inner-cache read in newton.jl — the resize length check, the stale-W rhs rescale from #4028, both compute_step! paths, and the nlstep build_solution — goes through NonlinearSolveBase.get_u/get_fu. No local workaround remains. Top-level fields the polyalg cache genuinely has (retcode, stats, prob, alg) are read as before, and not_terminated only touches force_stop/nsteps/maxiters, which are also top-level. The nlstep build_solution no longer attaches the inner trace (no polyalg accessor exists; the transient solution only feeds nlprobmap). The compat floor is raised to "2.39", and get_u/get_fu are added to the QA non-public-import allowlist alongside not_terminated.

Regression test nsa_polyalg_tests.jl runs TRBDF2 and FBDF with a RobustMultiNewton inner solver and checks both against the analytic solution of a stiff linear system to 1e-4, plus retcodes. Against the registered NonlinearSolveBase (pinned to the 2.39.0 floor): 8/8 new, and unchanged elsewhere — newton_tests 6/6, nsa_jacobian_reuse 17/17, nsa_matrixfree 14/14, nsa_smooth_est 10/10, nsa_sparse 10/10, nsa_stats 24/24, QA 16/16, all identical to master.

The #3859 MWE (Robertson, TRBDF2(nlsolve = NonlinearSolveAlg(RobustMultiNewton(autodiff = AutoForwardDiff()))), reltol=1e-6, abstol=1e-9) throws FieldError on master and returns Success here, with u(1e5) = [0.0178650, 7.2744e-8, 0.9821349] against a default-FBDF reference of [0.0178659, 7.2748e-8, 0.9821340] (5e-5 relative). The active branch is TrustRegion, and the run is step-for-step identical to NonlinearSolveAlg(TrustRegion()).

Scope: this fixes the crash, not every polyalgorithm. FastShortcutNonlinearPolyalg no longer throws, but its leading branches are Jacobian-free quasi-Newton methods, which do not produce a usable stage step under the one-step!-per-outer-iteration driving mode; the integrator reports Unstable/MaxIters rather than a wrong answer. Polyalgorithms combined with a matrix-free W (linsolve = KrylovJL_GMRES()) are still refused at init by NonlinearSolveBase, identically on master, because _nlalg_with_linsolve cannot push a linsolve into a polyalgorithm; the related SciML/NonlinearSolve.jl#1132 is still open. Both are better addressed by the #3817 redesign (drive inner solvers by solve! per stage) than by more glue here.

AI Disclosure

Claude assisted with this work.

@singhharsh1708
singhharsh1708 force-pushed the fix-nonlinearsolvealg-polyalg-3859 branch 2 times, most recently from 7dd8e8a to 4ec539e Compare July 25, 2026 19:18
Comment thread lib/OrdinaryDiffEqNonlinearSolve/src/newton.jl Outdated
@singhharsh1708
singhharsh1708 force-pushed the fix-nonlinearsolvealg-polyalg-3859 branch 2 times, most recently from 54a5ba9 to 49dec7d Compare July 31, 2026 20:09
NonlinearSolvePolyAlgorithmCache (RobustMultiNewton,
FastShortcutNonlinearPolyalg, ...) keeps u/fu on the active branch
cache instead of as top-level fields, so every raw cache.cache.u-style
read in newton.jl threw a FieldError. Read the inner iterate and
residual through NonlinearSolveBase.get_u/get_fu, which resolve to the
active branch for polyalgorithm caches and are plain field reads
otherwise.

The nlstep solution no longer carries the inner trace: it only feeds
nlprobmap, and polyalgorithm caches expose no accessor for a trace.

A second bug lived upstream: the polyalgorithm cache's reinit! left
force_stop/retcode set once any branch had converged, so every later
step! short-circuited on not_terminated and silently reused a stale
iterate. NonlinearSolveBase 2.39 resets both in reinit!, so no local
reset is needed; the compat floor bump follows once it is registered.
@singhharsh1708
singhharsh1708 force-pushed the fix-nonlinearsolvealg-polyalg-3859 branch from 49dec7d to 68b360c Compare August 1, 2026 21:26
@ChrisRackauckas
ChrisRackauckas merged commit dc19947 into SciML:master Aug 1, 2026
157 of 171 checks passed
@singhharsh1708
singhharsh1708 deleted the fix-nonlinearsolvealg-polyalg-3859 branch August 1, 2026 22:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants